package org.boot.thread;

import org.boot.thread.atomic.Account;
import org.junit.Before;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.math.BigDecimal;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.atomic.AtomicReference;

import static org.boot.thread.constant.ThreadConstant.*;

/**
 * 原子类相关操作
 *
 * @author 李坤
 * @date 2022/8/26 15:03
 */
public class AtomicOperate {

    private final AtomicReference<Account> reference = new AtomicReference<>(new Account("1", BigDecimal.ZERO));
    // 非线程安全/线程安全的账户信息
    private final Account account = new Account("1", BigDecimal.ZERO);
    // 默认的日志处理器
    private final Logger logger = LoggerFactory.getLogger(AtomicOperate.class);
    // 默认的线程池
    private final ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();

    @Before
    public void init() {
        // 缓冲队列(执行缓冲任务的队列)
        this.threadPoolTaskExecutor.setQueueCapacity(0);
        // 核心线程数
        this.threadPoolTaskExecutor.setCorePoolSize(CORE_POOL_SIZE);
        // 最大线程数
        this.threadPoolTaskExecutor.setMaxPoolSize(Integer.MAX_VALUE);
        // 线程空闲时间(超时后将销毁非核线程)
        this.threadPoolTaskExecutor.setKeepAliveSeconds(KEEP_ALIVE_SECONDS);
        // 线程拒绝策略(重试添加当前的任务直到成功)
        this.threadPoolTaskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());

        // 线程名称前缀
        this.threadPoolTaskExecutor.setThreadNamePrefix(THREAD_NAME_PREFIX);
        // 初始化线程池
        this.threadPoolTaskExecutor.initialize();
    }

    @Test
    public void unsafeAccount() throws InterruptedException {
        final CountDownLatch countDownLatch = new CountDownLatch(25);

        for (int i = 0; i < 25; i++) {
            CompletableFuture.runAsync(() -> {
                logger.debug("当前账户余额：" + account.unsafeTrade(BigDecimal.ONE));
                countDownLatch.countDown();
            }, threadPoolTaskExecutor);
        }

        countDownLatch.await();
        logger.info("最终账户余额：" + account.getBalance());

    }

    @Test
    public void safeAccount() throws InterruptedException {
        final CountDownLatch countDownLatch = new CountDownLatch(25);

        for (int i = 0; i < 25; i++) {
            CompletableFuture.runAsync(() -> {
                logger.debug("当前账户余额：" + account.safeTrade(BigDecimal.ONE));
                countDownLatch.countDown();
            }, threadPoolTaskExecutor);
        }

        countDownLatch.await();
        logger.info("最终账户余额：" + account.getBalanceReference().get());
    }

    @Test
    public void doubleUnsafeAccount() throws InterruptedException {
        final CountDownLatch countDownLatch = new CountDownLatch(25);

        for (int i = 0; i < 25; i++) {
            CompletableFuture.runAsync(() -> {
                // 错误用法，updateAndGet 内部调用的是 compareAndSet 逻辑，应该返回一个新的对象
                final Account account = reference.updateAndGet((prev) -> {
                    prev.setBalance(prev.getBalance().add(BigDecimal.ONE));
                    return prev;
                });
                logger.debug("当前账户余额：" + account.getBalance());
                countDownLatch.countDown();
            }, threadPoolTaskExecutor);
        }

        countDownLatch.await();
        logger.info("最终账户余额：" + reference.get().getBalance());
    }

    @Test
    public void doubleSafeAccount() throws InterruptedException {
        final CountDownLatch countDownLatch = new CountDownLatch(25);

        for (int i = 0; i < 25; i++) {
            CompletableFuture.runAsync(() -> {
                // 正确用法，需要注意 compareAndSet 的实现逻辑
                final Account account = reference.updateAndGet((prev) -> new Account(prev.getId(), prev.getBalance().add(BigDecimal.ONE)));
                logger.debug("当前账户余额：" + account.getBalance());
                countDownLatch.countDown();
            }, threadPoolTaskExecutor);
        }

        countDownLatch.await();
        logger.info("最终账户余额：" + reference.get().getBalance());
    }

}
